package com.google.bitcoin.core;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

import javax.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * @author apetersson
 */
public class MultisigPubkeys implements OutputDestination { //todo is serialization needed?

    private final int required;
    //todo make immutable List
    private final List<PubKey> eligible;

    //todo switch to PubKey as type here too..
    public MultisigPubkeys(List<byte[]> eligible, int required) {
        int numEligible = eligible.size();
        Preconditions.checkArgument(required >= 1, "required must be a positive number between 1 and 3");
        Preconditions.checkArgument(numEligible == 2 || numEligible == 3, "you must supply 2 or 3 public keys");
        Preconditions.checkArgument(required <= numEligible,
                "number of required addresses %s is greater than the numberof eligible addresses %s "
                , required, numEligible);
        this.required = required;
        this.eligible = ImmutableList.copyOf(Lists.transform(eligible, new Function<byte[], PubKey>() {
            public PubKey apply(@Nullable byte[] input) {
                return PubKey.valueOf(input);
            }
        }));
    }

    public static MultisigPubkeys from(ECKey key1, ECKey key2, int required) {
        return new MultisigPubkeys(Arrays.asList(key1.getPubKey(), key2.getPubKey()), required);
    }

    public int getEligibleSize() {
        return eligible.size();
    }

    public int getRequiredSize() {
        return required;
    }

    public List<PubKey> getEligiblePubkeys() {
        return eligible;
    }

    public boolean containsPubkey(final PubKey pubKey) {
        for (PubKey key : eligible) {
            if (key.equals(pubKey)) return true;
        }
        return false;
    }

    public byte[] createOutputScript() {
        try {
            int size = 3 + getEligibleSize();
            ByteArrayOutputStream bits = new UnsafeByteArrayOutputStream(size);
            bits.write(Opcodes.op_X_fromConstant(getRequiredSize()));
            for (PubKey pubkey : getEligiblePubkeys()) {
                Opcodes.writeBytes(bits, pubkey.getBytes());
            }
            bits.write(Opcodes.op_X_fromConstant(getEligibleSize()));
            bits.write(Opcodes.OP_CHECKMULTISIG);
            return bits.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
